﻿using System;
using System.Collections.Generic;
using System.Data;
using accesoDatos;
using clasesBase;

namespace Logica
{
    public class Formula : EntidadAlmacenable
    {
        /* --------------------- Atributos de la clase. Recordar que hay atributos heredados --------------------- */
        private String nombre = "", observacion = "";
        private DateTime fecha = ValoresGlobales.FechaMinima;
        private bool formulaActual = false;
        private int idProducto = 0;

        private List<ItemFormula> items;
        private List<InstruccionFormula> instrucciones;

        public const string tablaBD = "formula", columnaID = "id";


        /* --------------------- Propiedades --------------------- */
        public int ID
        {
            get { return this.id; }
            set { this.id = value; }
        }

        public int IdProducto
        {
            get { return this.idProducto; }
            set { this.idProducto = value; }
        }

        public String Nombre
        {
            get { return nombre; }
            set { nombre = value; }
        }

        public String Observacion
        {
            get { return observacion; }
            set { observacion = value; }
        }

        public DateTime Fecha
        {
            get { return fecha; }
            set { fecha = value; }
        }

        public bool FormulaActual
        {
            get { return formulaActual; }
            set { formulaActual = value; }
        }

        public List<ItemFormula> Items
        {
            get { return this.items; }
            set { this.items = value; }
        }

        public List<InstruccionFormula> Instrucciones
        {
            get { return this.instrucciones; }
            set { this.instrucciones = value; }
        }


        /* --------------------- Constructores --------------------- */
        public Formula()
            : this(0, true) // Llamo al constructor que recibe un parámetro para reutilizar el código del mismo
        { }

        public Formula(int idElem)
            : this(idElem, true) // Llamo al constructor que recibe un parámetro para reutilizar el código del mismo
        { }

        /* IMPORTANTE:
             ** Si el campo identificador unívoco de la tabla actual tuviese un nombre distinto de "id", se lo debe setear en este momento
             * (atributo heredado: nombreColumnaID)
             ** Si el campo identificador unívoco NO se generase automáticamente, se lo debe definir en este momento, cambiando el atributo
             * heredado idAutoGenerado por FALSE.
             */
        public Formula(int idElem, bool cargarListas)
            : base() // Llamo al constructor padre en primer lugar, para inicializar los valores correspondientes
        {
            // Es importante esta asignación ya que estos valores se utilizan tanto en métodos del objeto padre como éste
            this.nombreTablaBD = tablaBD;
            this.nombreColumnaID = columnaID;
            this.id = idElem;

            this.relacionesOtrasTablas.Add(ItemFormula.tablaBD, "idFormula");
            this.relacionesOtrasTablas.Add(InstruccionFormula.tablaBD, "idFormula");

            // Si se hubiese enviado un id != 0, entonces se buscan los datos del elemento en la BD para cargarlos al objeto actual que se está creando
            if (this.id != 0)
            {
                DataRow elemento = this.buscarElemento(id);
                try
                {
                    this.nombre = elemento["nombre"].ToString().Trim();
                    this.observacion = elemento["observacion"].ToString().Trim();
                    this.fecha = Convert.ToDateTime(elemento["fecha"]);
                    this.formulaActual = Convert.ToBoolean(elemento["formulaActual"]);
                    this.idProducto = Convert.ToInt32(elemento["idProducto"]);
                }
                catch (Exception ex)
                {
                    this.error = ex.Message;
                }
            }

            if (cargarListas) cargarColecciones();
            else
            {
                this.items = new List<ItemFormula>();
                this.instrucciones = new List<InstruccionFormula>();
            }
        }

        public void cargarColecciones()
        {
            this.items = ItemFormula.listar(new string[] { "idFormula" }, new string[] { "E" + this.id.ToString() });
            this.instrucciones = InstruccionFormula.listar(new string[] { "idFormula" }, new string[] { "E" + this.id.ToString() });
        }


        /* --------------------- Métodos --------------------- */
        public static List<Formula> listar(String[] columnasFiltros, String[] valoresFiltros)
        {
            List<Formula> listado = new List<Formula>();
            DataTable tabla = EntidadAlmacenable.listarElementos(tablaBD, columnasFiltros, valoresFiltros);
            try
            {
                foreach (DataRow fila in tabla.Rows)
                {
                    Formula elemento = new Formula(Convert.ToInt32(fila[columnaID]), true);
                    listado.Add(elemento);
                }
            }
            catch (Exception ex)
            {
            }

            return listado;
        }

        public static DataTable listaExtendida(string[] columnasFiltros, string[] valoresFiltros)
        {
            string camposConsulta = string.Format("{0}.*, CASE WHEN {0}.formulaActual = 1 THEN  \"Si\" ELSE \"No\" END AS actual, {1}.nombre as Producto", tablaBD, Producto.tablaBD);
            string[] tablasJoin = new string[] { string.Format("join {1} on {0}.idProducto = {1}.{2}", tablaBD, Producto.tablaBD, Producto.columnaID) };

            ManejadorBD manejador = new ManejadorBD();
            DataSet conjuntoDatos = manejador.obtenerDatos(camposConsulta, tablaBD, tablasJoin, columnasFiltros, valoresFiltros, new string[] { });

            DataTable datosListado;
            if (conjuntoDatos.Tables.Count > 0) datosListado = conjuntoDatos.Tables[0];
            else datosListado = new DataTable("Tabla");

            return datosListado;
        }
        
        public String guardar()
        {
            String[] columnas = new String[] { columnaID, "nombre", "idProducto", "fecha", "observacion", "formulaActual" };
            String[] valores = new String[] { this.id.ToString(), this.nombre.Trim().ToUpper(), this.idProducto.ToString(), 
                Validador.formatearFecha(this.fecha), this.observacion.Trim(), Validador.formatearBool(this.formulaActual) };

            string error = "";

            /* Si la fórmula que va a guardarse se marcó como actual, entonces por las dudas quito todas las posibles marcas de "formulaActual"
             * de las demás fórmulas asociadas al producto antes de continuar. */
            if (this.formulaActual) error = quitarFormulaActual();

            if (error.Equals(""))
            {
                error = base.guardarElemento(columnas, valores);
                if (error.Equals("")) // Si se guardó correctamente la fórmula, continúo y guardo los items e instrucciones
                {
                    string errorItem = "";
                    foreach (ItemFormula item in this.items)
                    {
                        item.IdFormula = this.id;
                        errorItem = " " + item.guardar();
                    }
                    if (!errorItem.Trim().Equals("")) error += "Errores ocurridos al insertar los ítems: " + errorItem;

                    string errorInstruccion = "";
                    foreach (InstruccionFormula instruccion in this.instrucciones)
                    {
                        instruccion.IdFormula = this.id;
                        errorInstruccion += " " + instruccion.guardar();
                    }
                    if (!errorInstruccion.Trim().Equals("")) error += "Errores ocurridos al insertar las instrucciones: " + errorInstruccion;
                }
            }

            return error;
        }

        private string quitarFormulaActual()
        {
            /* Siempre el primer campo que debe  */
            string[] columnasTablaActualizar = new string[] { columnaID, "formulaActual" };
            string[] valoresColumnasActualizar = new string[] { "0", "false" };

            this.manejadorBD.ejecutarABM(TipoOperacionABM.MODIFICACION, this.nombreTablaBD, columnasTablaActualizar, valoresColumnasActualizar, new string[] { "idProducto" }, new string[] { this.idProducto.ToString() });
            return manejadorBD.Error;
        }
    }
}